
// disable warnings on MSVC when using boost::split
#if defined(_MSC_VER) && _MSC_VER >= 1400 
#pragma warning(push) 
#pragma warning(disable:4996) 
#endif 

#include "XMLTools.h"
#include "Exceptions.h"

#include <boost/algorithm/string.hpp>
#include <filesystem>
#include <boost/lexical_cast.hpp>

#include "RapidXML/rapidxml.hpp"
#include "MathTools.h"
#include <cmath>
#include <fstream>

using std::cout;
using std::endl;
#if !defined(_WIN32)
using namespace std::filesystem;
#endif

namespace MMM
{
namespace XML
{

    std::string toString(float f) {
        std::stringstream stream;
        stream << f;
        return stream.str();
    }

    std::string toString(const Eigen::VectorXf &vec, const std::string &delimiter) {
        int size = (int)vec.rows();

        std::stringstream stream;
        for(int i = 0; i < size; ++i) {
            stream << vec[i] << delimiter;
        }
        return stream.str().substr(0,stream.str().size()-1);
    }

    Eigen::VectorXf MMM_IMPORT_EXPORT readFloatArray(std::string floatString, unsigned int arraySize)
    {
        std::vector<std::string> strs;

        boost::trim_if(floatString, boost::is_any_of("\t "));
        boost::split(strs, floatString, boost::is_any_of("\t "));

        if (strs.size() != arraySize) throw Exception::XMLFormatException("The string '" + floatString + "' should contain " + std::to_string(arraySize) + " float values.");

        Eigen::VectorXf res(arraySize);
        for (unsigned int i = 0; i < arraySize; i++)
        {
            res(i) = readFloat(strs[i].c_str());
        }
        return res;
    }

    float MMM_IMPORT_EXPORT readFloat(const char* floatString)
    {
        if (floatString == nullptr) throw Exception::XMLFormatException("Passing null string to readFloat(...)");

        std::stringstream floatStream;
        floatStream << std::string(floatString);
        float result;

        if (!(floatStream >> result)) throw Exception::XMLFormatException("The string '" + std::string(floatString) + "' can not be parsed into a float value");

        return result;
    }

    Eigen::Matrix4f MMM_IMPORT_EXPORT process4x4Matrix(RapidXMLReaderNodePtr matrixNode) {
        Eigen::Matrix4f matrix;
        for (int i = 1; i <= 4; i++) {
            std::string row = "row" + boost::lexical_cast<std::string>(i);
            RapidXMLReaderNodePtr rowNode = matrixNode->first_node(row.c_str());
            for (int j = 1; j <= 4; j++) {
                std::string column = "c" + boost::lexical_cast<std::string>(j);
                matrix(i - 1, j - 1) = convertToFloat((rowNode->attribute_value(column.c_str())).c_str());
            }
        }
        return matrix;
    }

    void MMM_IMPORT_EXPORT appendXML(const Eigen::Matrix4f &matrix, RapidXMLWriterNodePtr node, const std::string &name) {
        RapidXMLWriterNodePtr matrixNode = node->append_node(name);
        for (int i = 1; i <= 4; i++) {
            std::string row = "row" + boost::lexical_cast<std::string>(i);
            RapidXMLWriterNodePtr rowNode = matrixNode->append_node(row);
            for (int j = 1; j <= 4; j++) {
                std::string column = "c" + boost::lexical_cast<std::string>(j);
                rowNode->append_attribute(column, boost::lexical_cast<std::string>(matrix(i - 1, j - 1)));
            }
        }
    }

	/*template < >
	path& path::append< typename path::iterator >(typename path::iterator begin, typename path::iterator end, const codecvt_type& cvt)
	{
		for (; begin != end; ++begin)
			*this /= *begin;
		return *this;
	}*/
	
	// Return path when appended to a_From will resolve to same as a_To
	std::filesystem::path make_relative(std::filesystem::path a_From, std::filesystem::path a_To)
	{
		a_From = std::filesystem::absolute(a_From); a_To = std::filesystem::absolute(a_To);
		std::filesystem::path ret;
		std::filesystem::path::const_iterator itrFrom(a_From.begin()), itrTo(a_To.begin());
		// Find common base
		for (std::filesystem::path::const_iterator toEnd(a_To.end()), fromEnd(a_From.end()); itrFrom != fromEnd && itrTo != toEnd && *itrFrom == *itrTo; ++itrFrom, ++itrTo);
		// Navigate backwards in directory to reach previously found base
		for (std::filesystem::path::const_iterator fromEnd(a_From.end()); itrFrom != fromEnd; ++itrFrom)
		{
			if ((*itrFrom) != ".")
				ret /= "..";
		}
		// Now navigate down the directory branch
		//ret.append(itrTo, a_To.end());
		for (; itrTo != a_To.end(); ++itrTo)
			ret /= *itrTo;
		return ret;
	}

	std::string make_relative(std::string a_From, std::string a_To)
	{
        if(a_From.empty())
            a_From = ".";
		std::filesystem::path a = std::filesystem::canonical(std::filesystem::path(a_From));
		std::filesystem::path b = std::filesystem::canonical(std::filesystem::path(a_To));
        return make_relative(a, b).generic_string();
	}

	Eigen::VectorXf getFloatArray(const std::string &c, int size)
	{
		std::vector<std::string> strsOrig;
		std::vector<std::string> strs;
		std::string c2 = c;

		boost::split(strsOrig, c2, boost::is_any_of("\t "));

		for (auto & i : strsOrig)
		{
			if (!i.empty() && i != " ")
				strs.push_back(i);
		}


		if (strs.size()==0)
		{
			MMM_ERROR << "Error splitting " << c << " resulted in 0 substrings" << endl;
			return Eigen::VectorXf();
		}
		if (size>0  && (int)strs.size()<size)
		{
			MMM_ERROR << "Error splitting " << c << " resulted in " << strs.size() << " substrings, but requested " << size << endl;
			return Eigen::VectorXf();
		}

		int count = size;
		if (count<=0)
			count = (int)strs.size();

		Eigen::VectorXf res(count);
		for (int i=0;i<count;i++)
		{
			float f = convertToFloat(strs[i].c_str());
			res(i) = f;
		}
		return res;
	}


	std::string toLowerCase(const char* c)
	{
		if (nullptr == c)
		{
			MMM_ERROR << "Passing Null string to toLowerCase()" << endl;
			return std::string();
		}
		std::string res = c;
		toLowerCase(res);
		return res;
	}

	void toLowerCase(std::string& aString)
	{
		std::transform(aString.begin(), aString.end(), aString.begin(), tolower);
	}

    bool convertToBool(const std::string &s, const std::string &errorMsg)  {
        if (toLowerCase(s.c_str()) == "true" || s == "1") return true;
        else if (toLowerCase(s.c_str()) == "false" || s == "0") return false;
        else throw Exception::MMMException(errorMsg.empty() ? "Could not convert string '" + s + "' to bool" : errorMsg);
    }

	float convertToFloat(const char* s)
	{
		if (s==nullptr)
		{
			MMM_ERROR << "Passing Null string to convertToFloat()" << endl;
			return 0.0f;
		}
		std::stringstream floatStream;
		floatStream << std::string(s);
		float result;

		if (!(floatStream >> result))
		{
			MMM_WARNING << "The string '" << s << "' can not be parsed into a float value" << endl;
			return 0.0f;
		}

		return result;
	}

	float getUnitsScalingToMeter(rapidxml::xml_node<char>* node)
	{
		if (!node)
		{
			return 0.001f;
		}
		rapidxml::xml_attribute<> *attr = node->first_attribute();
		while (attr)
		{
			std::string an = toLowerCase(attr->name());
			if (an=="units" || an=="unit" || an=="unitslength")
			{
				std::string v = toLowerCase(attr->value());
				if (v=="mm" || v=="millimeter")
					return 0.001f;
                if (v == "cm" || v == "centimeter")
                    return 0.01f;
                if (v == "dm" || v == "decimeter")
                    return 0.1f;
                if (v == "m" || v == "meter")
                    return 1.0f;
                if (v == "km" || v == "kilometer")
                    return 1000.0f;
            }

			attr = attr->next_attribute();
		}
		return 0.001f;
	}


	float getUnitsScalingToRadian(rapidxml::xml_node<char>* node)
	{
		if (!node)
		{
			return 1.0f;
		}
		rapidxml::xml_attribute<> *attr = node->first_attribute();
		while (attr)
		{
			std::string an = toLowerCase(attr->name());
			if (an=="units" || an=="unit" || an=="unitsangle")
			{
				std::string v = toLowerCase(attr->value());
				if (v=="rad" || v=="rad")
					return 1.0f;
				if (v=="deg" || v=="degree")
					return (float)M_PI / 180.0f;
			}

			attr = attr->next_attribute();
		}
		return 1.0f;
	}


	float getUnitsScalingToKG(rapidxml::xml_node<char>* node)
	{
		if (!node)
		{
			return 1.0f;
		}
		rapidxml::xml_attribute<> *attr = node->first_attribute();
		while (attr)
		{
			std::string an = toLowerCase(attr->name());
			if (an=="units" || an=="unit" || an=="unitsweight")
			{
				std::string v = toLowerCase(attr->value());
				if (v=="ton" || v=="t")
					return 1000.0f;
				if (v=="g" || v=="gram")
					return 0.001f;
			}

			attr = attr->next_attribute();
		}
		return 1.0f;
	}


	Eigen::Matrix4f processTransformTag(rapidxml::xml_node<char>* transformXMLNode)
	{
		Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();
		if (!transformXMLNode)
		{
			MMM_ERROR << "NULL Child tag in XML definition" << endl;
			return transform;
		}

		rapidxml::xml_node<> *trXMLNode = nullptr;
		std::string nodeName = toLowerCase(transformXMLNode->name());
		if (nodeName=="transform")
			trXMLNode = transformXMLNode;
		else
		{
			trXMLNode = transformXMLNode->first_node("transform",0,false);
			if (!trXMLNode)
			{
				MMM_ERROR << "transformation node does not specify a <transform> tag" << endl;
				return transform;
			}
		}

		rapidxml::xml_node<>* node = trXMLNode->first_node();
		while (node)
		{
			std::string nodeName = toLowerCase(node->name());

			// Homogeneous Matrix 4x4
			if (nodeName=="matrix4x4")
			{
				Eigen::Matrix4f localT = Eigen::Matrix4f::Identity();
				rapidxml::xml_node<> *row1XMLNode = node->first_node("row1",0,false);
				rapidxml::xml_node<> *row2XMLNode = node->first_node("row2",0,false);
				rapidxml::xml_node<> *row3XMLNode = node->first_node("row3",0,false);
				rapidxml::xml_node<> *row4XMLNode = node->first_node("row4",0,false);
				if (row1XMLNode)
				{
					localT(0, 0) = getFloatByAttributeName(row1XMLNode, "c1");
					localT(0, 1) = getFloatByAttributeName(row1XMLNode, "c2");
					localT(0, 2) = getFloatByAttributeName(row1XMLNode, "c3");
					localT(0, 3) = getFloatByAttributeName(row1XMLNode, "c4");
				}

				if (row2XMLNode)
				{
					localT(1, 0) = getFloatByAttributeName(row2XMLNode, "c1");
					localT(1, 1) = getFloatByAttributeName(row2XMLNode, "c2");
					localT(1, 2) = getFloatByAttributeName(row2XMLNode, "c3");
					localT(1, 3) = getFloatByAttributeName(row2XMLNode, "c4");
				}

				if (row3XMLNode)
				{
					localT(2, 0) = getFloatByAttributeName(row3XMLNode, "c1");
					localT(2, 1) = getFloatByAttributeName(row3XMLNode, "c2");
					localT(2, 2) = getFloatByAttributeName(row3XMLNode, "c3");
					localT(2, 3) = getFloatByAttributeName(row3XMLNode, "c4");
				}

				if (row4XMLNode)
				{
					localT(3, 0) = getFloatByAttributeName(row4XMLNode, "c1");
					localT(3, 1) = getFloatByAttributeName(row4XMLNode, "c2");
					localT(3, 2) = getFloatByAttributeName(row4XMLNode, "c3");
					localT(3, 3) = getFloatByAttributeName(row4XMLNode, "c4");
				}
				float scaling = getUnitsScalingToMeter(node);

				localT(0,3) *= scaling;
				localT(1,3) *= scaling;
				localT(2,3) *= scaling;

				transform *= localT;
			} else if (nodeName == "matrix3x3")
			{
				// Rotation Matrix 3x3
				Eigen::Matrix4f localT = Eigen::Matrix4f::Identity();
				Eigen::Matrix3f m = process3x3Matrix(node);

				localT.block(0,0,3,3) = m;
				transform *= localT;
			} else if (nodeName == "rollpitchyaw" || nodeName == "rpy")
			{
				// ROLL PITCH YAW
				float r,p,y;
				r = p = y = 0.0f;
				r = getFloatByAttributeName(node, "roll");
				p = getFloatByAttributeName(node, "pitch");
				y = getFloatByAttributeName(node, "yaw");
	
				float scaling = getUnitsScalingToRadian(node);
				r = r*scaling;
				p = p*scaling;
				y = y*scaling;

				Eigen::Matrix4f localT = Eigen::Matrix4f::Identity();
				localT = Math::rpy2eigen4f(r,p,y);
				transform *= localT;
			} else if (nodeName == "quaternion" || nodeName == "quat")
			{
				// Quaternions
				float x,y,z,w;
				x = y = z = w = 0.0f;
				x = getFloatByAttributeName(node, "x");
				y = getFloatByAttributeName(node, "y");
				z = getFloatByAttributeName(node, "z");
				w = getFloatByAttributeName(node, "w");
				Eigen::Matrix4f r = Math::quat2eigen4f(x,y,z,w);
				transform *= r;
			} else if (nodeName == "translation" || nodeName == "trans")
			{
				// Translation
				Eigen::Matrix4f localT = Eigen::Matrix4f::Identity();
				localT(0,3) = getFloatByAttributeName(node, "x");
				localT(1,3) = getFloatByAttributeName(node, "y");
				localT(2,3) = getFloatByAttributeName(node, "z");
				float scaling = getUnitsScalingToMeter(node);
				localT(0,3) *= scaling;
				localT(1,3) *= scaling;
				localT(2,3) *= scaling;
				transform *= localT;
			} else if (nodeName == "dh")
			{
				// DH: Denavit–Hartenberg

				float theta = 0;
				float a = 0;
				float d = 0;
				float alpha = 0;

				float scalingRad = getUnitsScalingToRadian(node);
				float scalingM = getUnitsScalingToMeter(node);

				//std::cout << "scaling: " << scalingM << endl;

				theta = getFloatByAttributeName(node, "theta", true) * scalingRad;
				a = getFloatByAttributeName(node, "a", true) * scalingM;
				d = getFloatByAttributeName(node, "d", true) * scalingM;
				alpha = getFloatByAttributeName(node, "alpha", true) * scalingRad;

				//std::cout << "a: " << a << " - ";
				//std::cout << "d: " << d << endl;

				// compute DH transformation matrices
				Eigen::Matrix4f RotTheta = Eigen::Matrix4f::Identity();
				RotTheta(0, 0) = cos(theta);
				RotTheta(0, 1) = -sin(theta);
				RotTheta(1, 0) = sin(theta);
				RotTheta(1, 1) = cos(theta);
				Eigen::Matrix4f TransD = Eigen::Matrix4f::Identity();
				TransD(2, 3) = d;
				Eigen::Matrix4f TransA = Eigen::Matrix4f::Identity();
				TransA(0, 3) = a;
				Eigen::Matrix4f RotAlpha = Eigen::Matrix4f::Identity();
				RotAlpha(1, 1) = cos(alpha);
				RotAlpha(1, 2) = -sin(alpha);
				RotAlpha(2, 1) = sin(alpha);
				RotAlpha(2, 2) = cos(alpha);

				Eigen::Matrix4f localTransformation = RotTheta*TransD*TransA*RotAlpha;
				transform *= localTransformation;
			} else
			{
				MMM_INFO << "Ignoring unknown tag " << nodeName << endl;
			}
			node = node->next_sibling();
		}
		return transform;
	}

    float getFloatByAttributeName(rapidxml::xml_node<char>* xmlNode, const std::string& attributeName, bool optional)
    {
        if (!xmlNode)
        {
            MMM_ERROR << "getFloatByAttributeName got NULL data" << endl;
            return 0.0f;
        }
        rapidxml::xml_attribute<> *attr = xmlNode->first_attribute(attributeName.c_str(), 0, false);
        if (!attr)
        {
            if (!optional)
                MMM_ERROR << "The node <" << xmlNode->name() << "> does not contain an attribute named " << attributeName << endl;
            return 0.0f;
        }
        return convertToFloat(attr->value());
    }

    std::string getStringByAttributeName(rapidxml::xml_node<char>* xmlNode, const std::string& attributeName, bool optional)
    {
        if (!xmlNode)
        {
            MMM_ERROR << "getStringByAttributeName got NULL data" << endl;
            return std::string();
        }
        rapidxml::xml_attribute<> *attr = xmlNode->first_attribute(attributeName.c_str(), 0, false);
        if (!attr)
        {
            if (!optional)
                MMM_ERROR << "The node <" << xmlNode->name() << "> does not contain an attribute named " << attributeName << endl;
            return std::string();
        }
        return std::string(attr->value());
    }


	Eigen::Matrix3f process3x3Matrix(rapidxml::xml_node<char> *matrixXMLNode)
	{
		Eigen::Matrix3f m;
		m.setIdentity();
		if (!matrixXMLNode)
		{
			MMM_ERROR << "NULL matrix transform node?!" << endl;
			return m;
		}
		rapidxml::xml_node<> *row1XMLNode = matrixXMLNode->first_node("row1",0,false);
		rapidxml::xml_node<> *row2XMLNode = matrixXMLNode->first_node("row2",0,false);
		rapidxml::xml_node<> *row3XMLNode = matrixXMLNode->first_node("row3",0,false);
		if (row1XMLNode)
		{
			m(0, 0) = getFloatByAttributeName(row1XMLNode, "c1");
			m(0, 1) = getFloatByAttributeName(row1XMLNode, "c2");
			m(0, 2) = getFloatByAttributeName(row1XMLNode, "c3");
		}
		if (row2XMLNode)
		{
			m(1, 0) = getFloatByAttributeName(row2XMLNode, "c1");
			m(1, 1) = getFloatByAttributeName(row2XMLNode, "c2");
			m(1, 2) = getFloatByAttributeName(row2XMLNode, "c3");
		}

		if (row3XMLNode)
		{
			m(2, 0) = getFloatByAttributeName(row3XMLNode, "c1");
			m(2, 1) = getFloatByAttributeName(row3XMLNode, "c2");
			m(2, 2) = getFloatByAttributeName(row3XMLNode, "c3");
		}
		return m;
	}

    float getOptionalFloatByAttributeName(rapidxml::xml_node<char>* xmlNode, const std::string& attributeName, float standardValue)
    {
        if (!xmlNode)
        {
            MMM_ERROR << "getFloatByAttributeName got NULL data" << endl;
            return standardValue;
        }
        rapidxml::xml_attribute<> *attr = xmlNode->first_attribute(attributeName.c_str(), 0, false);
        if (!attr)
            return standardValue;
        return convertToFloat(attr->value());
    }

    bool getOptionalBoolByAttributeName(rapidxml::xml_node<char>* xmlNode, const std::string& attributeName, bool standardValue)
    {
        if (!xmlNode)
        {
            MMM_ERROR << "getOptionalBoolByAttributeName got NULL data" << endl;
            return standardValue;
        }
        rapidxml::xml_attribute<> *attr = xmlNode->first_attribute(attributeName.c_str(), 0, false);
        if (!attr)
            return standardValue;
        return isTrue(attr->value());
    }

    bool isTrue(const char* s)
    {
        std::string e = toLowerCase(s);
        if (e == "true" || e == "1" || e == "yes")
            return true;
        return false;
    }

	std::string toXML(const Eigen::Matrix3f &m, const std::string &tabs, bool skipMatrixTag)
	{
		std::stringstream ss;
		if (!skipMatrixTag)
			ss << tabs << "<Matrix3x3>\n";
		ss << tabs << "\t<row1 c1='" << m(0,0) << "' c2='" << m(0,1) << "' c3='" << m(0,2) << "'/>\n";
		ss << tabs << "\t<row2 c1='" << m(1,0) << "' c2='" << m(1,1) << "' c3='" << m(1,2) << "'/>\n";
		ss << tabs << "\t<row3 c1='" << m(2,0) << "' c2='" << m(2,1) << "' c3='" << m(2,2) << "'/>\n";
		if (!skipMatrixTag)
			ss << tabs << "</Matrix3x3>\n";
		return ss.str();
	}


	std::string toXML(const Eigen::Matrix4f &m, const std::string &tabs, bool skipMatrixTag)
	{
		std::stringstream ss;
		if (!skipMatrixTag)
			ss << tabs << "<Matrix4x4>\n";
		ss << tabs << "\t<row1 c1='" << m(0,0) << "' c2='" << m(0,1) << "' c3='" << m(0,2) << "' c4='" << m(0,3) << "'/>\n";
		ss << tabs << "\t<row2 c1='" << m(1,0) << "' c2='" << m(1,1) << "' c3='" << m(1,2) << "' c4='" << m(1,3) << "'/>\n";
		ss << tabs << "\t<row3 c1='" << m(2,0) << "' c2='" << m(2,1) << "' c3='" << m(2,2) << "' c4='" << m(2,3) << "'/>\n";
		ss << tabs << "\t<row4 c1='" << m(3,0) << "' c2='" << m(3,1) << "' c3='" << m(3,2) << "' c4='" << m(3,3) << "'/>\n";
		if (!skipMatrixTag)
			ss << tabs << "</Matrix4x4>\n";
		return ss.str();
	}

    std::string getPath(const std::string& filename)
	{
        std::filesystem::path f(filename);
        return f.parent_path().generic_string();
	}

	std::string getFileName(const std::string& filepath)
	{
		std::filesystem::path f(filepath);
		return f.filename().generic_string();
	}

    std::string getFileNameWithoutExtension(const std::string& filepath)
    {
        std::filesystem::path f(filepath);
        return f.stem().generic_string();
    }

    std::string getFileExtension(const std::string& filepath)
    {
        std::filesystem::path f(filepath);
        return f.extension().generic_string();
    }

	std::string getAbsoluteFile(const std::string &filename)
	{
		std::filesystem::path filenameNew(filename);
        return std::filesystem::absolute(filenameNew).generic_string();
	}

    std::string getFolderName(const std::string &filepath)
    {
        std::filesystem::path f(filepath);
        return f.parent_path().filename().generic_string();
    }

	void makeAbsolutePath(const std::string &basePath, std::string &filename)
	{
		if (filename.empty())
			return;

		std::filesystem::path filenameNew(filename);
		std::filesystem::path filenameBasePath(basePath);

		std::filesystem::path filenameNewComplete = std::filesystem::operator/(filenameBasePath, filenameNew);
        filename = filenameNewComplete.generic_string();
	}

    bool isValidFile(const std::string &filename)
    {
        // check file absolute
        std::filesystem::path fn(filename);
        try {
            if (std::filesystem::exists(fn))
                return true;
        }
        catch (...){}
        return false;
    }


	void makeRelativePath(const std::string &basePath, std::string &filename)
	{
        if (filename.empty())
            return;

        namespace fs = std::filesystem;

        filename = make_relative(basePath,filename);

        /*
        fs::path diffpath;
        fs::path tmppath = fs::canonical(fs::path(filename));
        fs::path basePathDir = fs::canonical(fs::path(basePath));

        while (tmppath != basePathDir)
        {
            diffpath = tmppath.filename() / diffpath;
            tmppath = tmppath.parent_path();
            if (tmppath.empty())
            {
                // no relative path found, take complete path
                diffpath = filename;
                break;
            }
        }

        filename = diffpath.generic_string();*/
	}

	std::string processFileNode(rapidxml::xml_node<char> *fileNode, const std::string &basePath)
	{
		std::string result;
		if (!fileNode)
			return result;
		std::string fileName = fileNode->value();
		if (fileName.empty())
		{
			cout << "Invalid file defined in FILE tag" << endl;
			return result;
		}
		std::string pathStr("path");
		std::string pathAttribute;
		rapidxml::xml_attribute<char>* attr = fileNode->first_attribute();
		if (attr)
		{
			std::string attrName = toLowerCase(attr->name());
			if (attr->name() == pathStr)
			{
				pathAttribute = attr->value();
			}
		}
		if (!pathAttribute.empty())
		{
			pathAttribute = toLowerCase(pathAttribute.c_str());
			if (pathAttribute == "relative")
				makeAbsolutePath(basePath, fileName);
			else if (pathAttribute != "absolute")
			{
				cout << "Unknown path attribute in <File> tag:" << pathAttribute << endl;
			}
		}
		else
		{

			// check file absolute
			std::filesystem::path fn(fileName);
			try {
				if (std::filesystem::exists(fn))
					return fileName;
			}
			catch (...){}
			// check file relative
			std::string absFileName = fileName;
			makeAbsolutePath(basePath, absFileName);
			fn = absFileName;
			try {
				if (std::filesystem::exists(fn))
					return absFileName;
			}
			catch (...){}

			cout << "Could not determine valid filename from " << fileName << endl;
		}
		return fileName;
	}

	bool saveXML(const std::string &filename, const std::string &content)
	{
		std::ofstream outFile(filename.c_str());
		if (!outFile.is_open())
			return false;

		outFile << "<?xml version='1.0'?>" << endl;
		outFile << "<MMM>" << endl;
		outFile << content << endl;
		outFile << "</MMM>" << endl;
		outFile.close();
		return true;
	}


	void addXMLHeader(std::string &xmlString)
	{
		std::string h("<? xml version = '1.0' ?>\n");
		xmlString = h + xmlString;
	}
}



}

#if defined(_MSC_VER) && _MSC_VER >= 1400 
#pragma warning(pop) 
#endif

